#pragma once

#include <map>
#include <stack>
#include <string>

// 管理标号生成
class LabelManager {
        public:
			std::map<std::string, long> label_map;

			LabelManager() {
				label_map[std::string("")] = 0;
			}
            ~LabelManager() = default;

           	// 返回临时标号，并移动
   	       	long Pop() {
				return label_map[std::string("")]++;
            }

			std::string *PopTemp() {
				auto str = new std::string("%");
				*str += std::to_string(label_map[std::string("")]++);
				return str;
			}

			// 返回具名标号的序号，并移动
			long Pop(const std::string &ident) {
				if (label_map.find(ident) != label_map.end()) {
					return label_map[ident]++;
				} else {
					label_map[ident] = 1;
					return 0;
				}
			}

			// 带名字的具名标号
			std::string *PopNamed(const std::string &ident) {
				if (label_map.find(ident) != label_map.end()) {
					auto str = new std::string("@");
					*str += ident+"_"+std::to_string(label_map[std::string(ident)]++);
					return str;
				} else {
					label_map[ident] = 1;
					return new std::string("@"+ident+"_0");
				}
			}

			// 带名字的临时标号
			std::string *PopTemp(const std::string &ident) {
				if (label_map.find(ident) != label_map.end()) {
					auto str = new std::string("%");
					*str += ident+"_"+std::to_string(label_map[std::string(ident)]++);
					return str;
				} else {
					label_map[ident] = 1;
					return new std::string("%"+ident+"_0");
				}
			}
               
           	// 返回可获得的临时标号
            long Peek() {
				return label_map[std::string("")];
            }

			// 返回可获得的具名标号的序号
			long Peek(const std::string &ident) {
				if (label_map.find(ident) != label_map.end()) {
					return label_map[ident];
				} else {
					return 0;
				}
			}
};
static LabelManager label_manager;

typedef enum {
	VOID = 0,
	VARI32,
	CONSTI32
} IdentType;

class SymTabEntry {
	public:
		IdentType type;
		std::string val;
		std::string name;

		SymTabEntry():type(IdentType::VOID), val(""), name("")  {}
};

class SymTab {
	public:
		std::map<std::string, SymTabEntry> tab;

		void Insert(const std::string &key) {
			tab.insert({key, SymTabEntry()});
		}

		void Insert(const std::string &key,  const std::string &name) {
			tab[key].name = name;
		}

		void Insert(const std::string &key,  const SymTabEntry &entry) {
			tab[key] = entry;
		}

		void SetType(const std::string &key, IdentType type) {
			tab[key].type = type;
		}

		void SetVal(const std::string &key, const std::string &val) {
			tab[key].val = val;
		}

		bool Check(const std::string &key) {
			auto iter = tab.find(key);
			return iter != tab.end();
		}

		const SymTabEntry &Get(const std::string &key) {
			auto iter = tab.find(key);
			return iter->second; // 使用者负责在使用前检查 
		}
};

class SymTabManager {
	public:
		std::vector<SymTab> symtab_stk;

		SymTabManager() = default;
		~SymTabManager() = default;
		
		void Forward() {
			symtab_stk.push_back(SymTab());
		}

		void Backward() {
			symtab_stk.pop_back();
		}

		void Push(const std::string &ident) {
			long num = label_manager.Pop(ident);
			std::string name("@");
			name += ident + "_" + std::to_string(num);
			symtab_stk.back().Insert(ident, name);
		}

		bool SetType(const std::string &ident, IdentType type) {
			symtab_stk.back().SetType(ident, type);
			return true;
		}

		bool SetVal(const std::string &ident, const std::string &val) {
			symtab_stk.back().SetVal(ident, val);
			return true;
		}

		bool Check(const std::string &ident) {
			int cnt;
			for (cnt = symtab_stk.size()-1; cnt >= 0 && !symtab_stk[cnt].Check(ident); --cnt);
			return cnt >= 0;
		}

		const SymTabEntry &Get(const std::string &ident) {
			int cnt = 0;
			for (cnt = symtab_stk.size()-1; cnt > 0 && !symtab_stk[cnt].Check(ident); --cnt);
			if (cnt != symtab_stk.size() - 1) { // 由使用者负责检查是否定义
				symtab_stk.back().Insert(ident, symtab_stk[cnt].Get(ident)); // 与上级作用域同步类型
			}
			return symtab_stk.back().Get(ident);
		}
};

static SymTabManager symtab_manager;

class Quaternion {
	public:
		int id;
};

